import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dk_flutter_base/dk_core.dart';
import 'package:meta/meta.dart';
import 'package:quiver/strings.dart';
import 'package:rxdart/rxdart.dart';

typedef RequestFailCallback(DefaultHttpError error);
typedef RequestFailCallbackFull(DefaultHttpError error, dynamic originalError);
typedef RequestSuccessCallback(
    DefaultResponseData data, bool isBusinessSuccess);

///网络请求客户端基类
///通常需要混入ClientMixIn以实现该类的抽象方法
///
///提供了
///
///* 1.网络代理配置
///* 2.非release情况下的log日志
///* 3.通用[CancelToken]
///* 4.返回[StreamSubscription]类型的网络请求
///* 5.返回[Observable]类型的网络请求
///* 6.普通类型的网络请求
///* 7.添加拦截器的抽象方法
///* 8.网络成功的抽象方法
///* 9.网络失败的抽象方法
abstract class BaseHttpClient {
  Dio httpClient;
  BaseOptions netOptions;
  final CancelToken defaultCancelToken = CancelToken();

  CancelToken get cancelToken => defaultCancelToken;

  BaseHttpClient() {
    // 初始化
    httpClient = new Dio(netOptions);
    switch (Env?.value?.type) {
      case EnvType.RELEASE:
        break;
      case EnvType.DEBUG:
      case EnvType.COMMIT_TEST:
      case EnvType.PRE_RELEASE:
      default:
        httpClient.interceptors.add(LogInterceptor());
        break;
    }
    if (isNotEmpty(Env?.value?.netWorkConfig?.localProxyIPAddress)) {
      (httpClient.httpClientAdapter as DefaultHttpClientAdapter)
          .onHttpClientCreate = (HttpClient client) {
        client.findProxy = (uri) {
          return "PROXY ${Env?.value?.netWorkConfig?.localProxyIPAddress}:${Env?.value?.netWorkConfig?.localProxyPort}";
        };
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) => true;
        /*String proxy =
            "${Env?.value?.netWorkConfig?.localProxyIPAddress}:${Env?.value?.netWorkConfig?.localProxyPort}";
        client.findProxy = (uri) {
          Map<String, String> environment = Map();
          if (uri.scheme == "http") {
            environment["http_proxy"] = proxy;
          } else if (uri.scheme == "https") {
            environment["https_proxy"] = proxy;
          }
          return HttpClient.findProxyFromEnvironment(uri,
              environment: environment);
        };*/
      };
    }

    addInterceptor(httpClient.interceptors);
  }

  ///利用rxDart封装网络请求，返回[StreamSubscription]
  StreamSubscription<Response<Map<String, dynamic>>> request(
      {HttpOption httOption, Function successCallback, Function failCallback}) {
    return Observable.fromFuture(httpClient.request<Map<String, dynamic>>(
            httOption?.path,
            queryParameters: httOption?.queryMap,
            data: httOption?.data,
            cancelToken: httOption?.cancelToken ?? defaultCancelToken,
            options: httOption?.options,
            onSendProgress: httOption?.onSendProgress,
            onReceiveProgress: httOption?.onReceiveProgress))
        .listen((Response<Map<String, dynamic>> response) {
      checkSuccessResponse(response, successCallback, failCallback);
    }, onError: (error, StackTrace stackTrace) {
      checkError(error, successCallback, failCallback);
    }, cancelOnError: cancelOnError, onDone: onDone);
  }

  ///利用rxDart封装网络请求，返回[Observable]
  Observable<Response<Map<String, dynamic>>> requestWithObservable(
      {HttpOption httOption, Function successCallback, Function failCallback}) {
    return Observable.fromFuture(httpClient.request<Map<String, dynamic>>(
        httOption?.path,
        queryParameters: httOption?.queryMap,
        data: httOption?.data,
        cancelToken: httOption?.cancelToken ?? defaultCancelToken,
        options: httOption?.options,
        onSendProgress: httOption?.onSendProgress,
        onReceiveProgress: httOption?.onReceiveProgress));
  }

  ///普通网络请求
  void requestWithNormal(
      {HttpOption httOption,
      Function successCallback,
      Function failCallback}) async {
    try {
      Response<Map<String, dynamic>> response =
          await httpClient.request<Map<String, dynamic>>(httOption?.path,
              queryParameters: httOption?.queryMap,
              data: httOption?.data,
              cancelToken: httOption?.cancelToken ?? defaultCancelToken,
              options: httOption?.options,
              onSendProgress: httOption?.onSendProgress,
              onReceiveProgress: httOption?.onReceiveProgress);
      if (checkBool(httOption?.cancelToken?.isCancelled) ||
          defaultCancelToken.isCancelled) {
        return;
      }
      checkSuccessResponse(response, successCallback, failCallback);
    } on DioError catch (e) {
      checkError(e, successCallback, failCallback);
    }
  }

  ///添加拦截器
  void addInterceptor(Interceptors interceptors) {}

  ///网络成功后的响应
  void checkSuccessResponse(Response<Map<String, dynamic>> response,
      Function successCallback, Function failCallback);

  ///网络失败后的响应
  void checkError(dioError, Function successCallback, Function failCallback);

  ///rxDart封装网络请求返回[StreamSubscription]的onDone方法，默认空实现
  void onDone() {}

  ///rxDart封装网络请求返回[StreamSubscription]的cancelOnError参数，默认false
  bool cancelOnError = false;
}

///native传递过来的网络基本参数
///
///* baseUrl:该参数作为保留参数，仅为单一网络域名的app使用
///* appId:appId
///* appId:clientSecret
///* connectTimeout:连接超时设置
///* receiveTimeout:接收超时设置
///* localProxyIPAddress:代理地址
///* localProxyPort:代理端口，默认8888
class NetWorkConfig {
  @required
  String baseUrl;
  @required
  String appId;
  @required
  String clientSecret;

  int connectTimeout;
  int receiveTimeout;

  String localProxyIPAddress;
  String localProxyPort;

  NetWorkConfig(
      {this.baseUrl,
      this.appId,
      this.clientSecret,
      this.connectTimeout,
      this.receiveTimeout,
      this.localProxyIPAddress,
      this.localProxyPort = "8888"});
}

///发起网络请求的参数bean
///
///* path:请求的接口地址
///* queryMap:get和delete请求携带的参数，类型为[Map]
///* data:post和put请求携带的参数，类型通常为[Map]
///* cancelToken:取消网络请求的token
///* options:单个网络请求的配置参数，参考[Options],通常只设置请求类型
///* onSendProgress:发送进度的回调函数
///* onReceiveProgress:接收进度的回调函数
class HttpOption {
  String path;
  Map<String, dynamic> queryMap;
  dynamic data;
  CancelToken cancelToken;
  Options options;
  ProgressCallback onSendProgress;
  ProgressCallback onReceiveProgress;

  HttpOption(
    this.path, {
    this.queryMap,
    this.data,
    this.cancelToken,
    this.options,
    this.onSendProgress,
    this.onReceiveProgress,
  });

  HttpOption.get(
    this.path, {
    this.queryMap,
    this.cancelToken,
    this.options,
  }) {
    options = checkDioOptions("GET", options);
  }

  HttpOption.delete(
    this.path, {
    this.queryMap,
    this.cancelToken,
    this.options,
  }) {
    options = checkDioOptions("DELETE", options);
  }

  HttpOption.post(
    this.path, {
    this.data,
    this.cancelToken,
    this.options,
  }) {
    options = checkDioOptions("POST", options);
  }

  HttpOption.put(
    this.path, {
    this.data,
    this.cancelToken,
    this.options,
  }) {
    options = checkDioOptions("PUT", options);
  }
}
